# -*-coding utf-8 -*-

__project__ = 'le-bo16'
__file_name__ = 'class12'
__author__ = 'hqDu'
__time__ = '2020-11-27 22:22'
__product_name = 'PyCharm'
# @fileName :class12.py

class Dog(object):
    # 属性
    def __init__(self,name):  # 不会有返回值return
        self.name = name
    # 方法
    def __str__(self):
        """
        只能有默认形参，必须return一个字符串
        :return: 一个字符串
        print(对象)，就会调用此方法；若类内没有此方法，print(对象)打印对象的十六进制内存地址
        用于追踪对象的属性信息变化，打印属性用的
        实际工作不写，相当于debug，print
        可以用于返回报错信息
        """
        # return 'mingzi', self.name  # 返回元组，它把self.name看成了元组，并且逗号','隔绝了两端内容【字符串,元组】。但是__str__要求返回一个字符串
        return '名字:%s' % self.name

    # 监听对象是否被销毁
    def __del__(self):
        """
        当删除对象时，python解释器也会默认调用一个方法，这个方法为__del__()方法
        单个对象的引用计数为0的情况下，销毁对象的时候，会调用此方法。
        杀死对象，释放内存
        运行中，当程序运行结束了，对象的引用计数为0了，所以就自动调用了__del__
        但是实际中，服务器不会关闭的，所以需要手动调用del(对象)，才会销毁对象，释放内存；所以需要监听对象是否被销毁
        :return: 
        """
        print('!!!!!')

# 情况1，程序未结束的情况下，没有del
# wangcai = Dog('hahah')
# print(wangcai)
# input()  # 此时不会打印 !!!!!，程序还未结束

# 情况2：程序不结束，通过del调__del__魔法方法
# wangcai = Dog('hahah')
# print(wangcai)
# # 程序不结束，但是还想调__del__魔法方法
# del wangcai
# # input()  # 此时不会打印 !!!!!，程序还未结束

# 情况3：存在对象以及对象的引用的情况，如果引用对象的变量与对象是一致的，那么del会直接释放对象的内存
# wangcai = Dog('hahah')
# print(wangcai)
# wangcai = wangcai  # 对象wangcai的引用计数为2
# del wangcai
# input()

# 情况4：存在对象以及对象的引用的情况，如果引用对象的变量与对象是不一致的，那么需要分别del对象，以及引用对象的变量
# 这种情况，只会调用__del__一次，即对象的引用计数为0的时候
# 程序未结束的情况下，del对象以及对象的引用，才会调用__del__方法；
# 程序未结束的情况下，但是，如果对象的引用计数不为0，那么就不会调用__del__方法
# wangcai = Dog('hahah')
# print(wangcai)
# wang = wangcai  # 对象wangcai的引用计数为2
# del wangcai
# del wang
# input()

# 情况5：程序未结束的情况下，del两个不同的对象，会调用__del__方法两次
# wangcai = Dog('hahah')
# print(wangcai)
# wang = Dog('heihei')
# print(wang)
# del wangcai
# del wang
# input()
#
# wangcai = Dog('haha')
#
# # __name__相当于class13.py，被调用的模块
# # '__main__'相当于当前模块的名字，即执行程序的模块
# # 当从一个模块中调用另一个模块的内容时，会不一致
# if __name__ == '__main__':  # 单元测试一个模块时会使用
#     print(wangcai)

# import hqdu.try
# 直接run，就会执行hqdu文件夹下的try.py的print("11111111")
# 但是不会执行if语句内的内容，因为__name__指的是模块try.py，而'__main__'指的是执行程序的模块class13.py


# 单继承：子类只继承一个父类
"""
2.3
单继承：子类只继承一个父类
故事情节：煎饼果子老师傅在煎饼果子界摸爬滚打几十年，拥有一身精湛的煎饼果子技术，
并总结了一套"古法煎饼果子配方"。

可是老师傅年迈已久，在嗝屁之前希望把自己的配方传承下去，于是老师傅把配方传给他的徒弟大猫..
"""
class Master(object):
    def __init__(self):
        self.peifang = '古法配方'
    def make_cake(self):
        print('制作古法煎饼')

    def aaa(self):
        print('aaa')

# Li = Master()
# print(Li.peifang)
# Li.make_cake()

# class Prent(Master):
#     pass
#
# damao = Prent()
# print(damao.peifang)
# damao.make_cake()

# 子类继承了父类，并继承了属性和方法
# 虽然子类没有定义__init__方法初始化属性，也没有定义实例方法，
# 但是父类有。所以只要创建子类的对象，就默认执行了那个继承过来的__init__方法


# 多继承
"""
2.4 多继承
大猫掌握了师傅的配方，可以制作古法煎饼果子。但是大猫是个爱学习的好孩子，
他希望学到更多的煎饼果子的做法，于是通过百度搜索，找到了一家煎饼果子培训学校。
（多继承）
"""
class School(object):
    def __init__(self):
        self.peifang = '现代配方'

    def make_cake(self):
        print("现代配方")

    def bb(self):
        print('bbb')

class Prentice(Master, School):
    pass

# 对于父类中相同的属性和方法，按照子类的定义中，先写的父类，其属性和方法得到优先调用；先来后到
print(Prentice.__mro__)  # 魔法属性__mro__，看类的执行顺序
# (<class '__main__.Prent'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)
# 如果子类内有方法和属性，优先调用子类的；如果子类没有，则依次查找第一个父类，第2个父类...直到第一次找到该属性或方法
damao = Prentice()
print(damao.peifang)
damao.make_cake()
damao.aaa()
damao.bb()


"""
多继承可以继承多个父类，也继承了所有父类的属性和方法
注意：如果多个父类中有同名的 属性和方法，则默认使用第一个父类的属性和方法（根据类的魔法属性mro的顺序来查找）
多个父类中，不重名的属性和方法，不会有任何影响
"""